/*******************************************************************************
 * Copyright (c) 2012, 2013 XML-Print Project.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     XML-Print Project - initial API and implementation
 ******************************************************************************/
package info.xmlprint.core.referencesystem;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;

/*
 * Base data model for ReferenceFieldGroup and ReferenceSystemGroup
 */
public abstract class AbstractBaseGroup implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = -46370170083228972L;
	private boolean needRunUpdate = false;
	private String[] names;

	private static Logger logger = Logger.getLogger(AbstractBaseGroup.class
			.getName());

	private List<AbstractBaseItem> list = new ArrayList<AbstractBaseItem>();

	// xml root element name, must defined in subclass
	protected String rootElementName;

	// childnode: <reference> or <field>, must defined in subclass
	protected String typeElementName;

	public AbstractBaseGroup() {
		setRootElementName();
	}

	public void add(AbstractBaseItem item) {
		needRunUpdate = true;
		list.add(item);
		item.setGroup(this);
	}

	/*
	 * AbstractBaseItem nach xmlDocument transformieren
	 */
	public Document getDoc() {
		Document doc = DocumentHelper.createDocument();
		Element root = doc.addElement(rootElementName);
		Element temp = null;

		for (AbstractBaseItem item : getItems()) {
			// iterate over each referencing system
			Element ref = root.addElement(typeElementName);
			for (String f : item.valueMap.keySet()) {
				// iterate over each element of the recent referencing system
				// setup a new document and parse the content
				Document document = DocumentHelper.createDocument();
				temp = document.addElement(f);
				try {
					String content = item.getValueOf(f);
					document = DocumentHelper
							.parseText("<temp>" + content + "</temp>");//$NON-NLS-1$
				} catch (DocumentException e) {
					logger.error(e);
					e.printStackTrace();
				}
				for (int i = 0, size = document.getRootElement().nodeCount(); i < size; i++) {
					Node newNode = (Node) document.getRootElement().node(i)
							.clone();
					temp.add(newNode);
				}
				ref.add(temp);
			}
		}
		return doc;
	}

	public AbstractBaseItem getItem(String name) {
		for (AbstractBaseItem field : list) {
			if (field.getValueOf("name").equals(name)) {//$NON-NLS-1$
				return field;
			}
		}
		return null;
	}

	public int getItemCount() {
		return list.size();
	}

	public String[] getItemNames() {
		if (needRunUpdate) {
			update();
		}
		return names;
	}

	public List<AbstractBaseItem> getItems() {
		if (needRunUpdate) {
			update();
		}
		return list;
	}

	public boolean has(AbstractBaseItem field) {
		return list.contains(field);

	}

	public boolean hasItem(String name) {
		if (needRunUpdate) {
			update();
		}
		if (names != null) { 
			for (String s : names) {
				if (s.equals(name)) {
					return true;
				}
			}
		}
		return false;
	}

	public void remove(AbstractBaseItem field) {
		needRunUpdate = true;
		list.remove(field);
		field.setGroup(null);
	}

	public abstract void setRootElementName();

	private void update() {
		Collections.sort(list, new Comparator<AbstractBaseItem>() {
			@Override
			public int compare(AbstractBaseItem f1, AbstractBaseItem f2) {
				return f1.getValueOf("name").compareToIgnoreCase(
						f2.getValueOf("name"));//$NON-NLS-1$
			}
		});

		names = new String[list.size()];
		for (int i = 0; i < list.size(); i++) {
			names[i] = list.get(i).getValueOf("name");//$NON-NLS-1$
		}
		needRunUpdate = false;
	}

	/*
	 * transform xmlDocument into AbstractBaseItemGroup
	 */
	public void updateByDoc(Document doc) {
		if (doc == null) {
			return;
		}
		Element docRoot = doc.getRootElement();
		Node typeRoot = docRoot.selectSingleNode(rootElementName);
		if (typeRoot != null) {
			for (Object obj : typeRoot.selectNodes(typeElementName).toArray()) {
				Element elem = (Element) obj;
				AbstractBaseItem abi = null;
				if (this instanceof ReferenceFieldGroup) {
					abi = new ReferenceField();
				} else if (this instanceof ReferencingSystemGroup) {
					abi = new ReferencingSystem();
				} else if (this instanceof PageMasterGroup) {
					abi = new PageMaster();
				}
				
				if (abi != null) {
					abi.updateByElement(elem);
				}
				add(abi);
			}
		}

	}

}
